Skip to content

Conversation

@paulo-ferraz-oliveira
Copy link
Collaborator

@paulo-ferraz-oliveira paulo-ferraz-oliveira commented Jul 15, 2025

Description

This pull request is all about:

  1. configuration validation (check On config. validation #508)
  2. preparation for some of the upcoming ergonomics/UX changes we have identified in Issues

First of all, I want to acknowledge this is a big change and some (much?) stuff can change before it's considered for merging.

Two things stand out, to me:

  1. I rebased on the latest 6 commits to the main branch and had 0 conflicts, which shows it's somewhat self-contained (even if BIG)
  2. I want to update rebar3_lint and elvis in tandem with this, before it's considered for merging, because this a. touches on the API the aforementioned use, b. hopefully simplifies and gives utilities for a more unified experience in the Elvis ecosystem.

Required changes

Closes #438.
Closes #439.


Further considerations

As always:

  1. I'm opening this as draft but Ok to get comments on it
  2. I haven't written tests yet, but have imagined how I'll do it (I basically adapted, up to now, the code for the tests to pass, trying to change them slightly, but not fundamentally)
  3. I'll self-review my choices to allow for further discussion points, and these might also lead to further changes I'll eventually push

Copy link
Member

@elbrujohalcon elbrujohalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments…

@paulo-ferraz-oliveira
Copy link
Collaborator Author

paulo-ferraz-oliveira commented Jul 16, 2025

@elbrujohalcon, I'm doing "I have decided on showing results without throwing (kind of the current behaviour which prints sometimes and throws sometimes), and adapted tests to that".

My decision is to not throw for invalid_config, but return {fail, _}.

This:

  1. eases consumption (the API is more predicable with a tuple than with a throw)
  2. eases tests (no more unwarranted try-catchs)
  3. lays the foundation for upcoming warnings_as_errors (for the time being, though, if there's issues detected with the configuration, elvis_core will signal it and analysis stops)

Edit: the remaining, explicit, throws are enoent and invalid_file for which we could apply a similar behaviour, in the future, if we wanted.

@paulo-ferraz-oliveira
Copy link
Collaborator Author

I've self-reviewed and (based on @bormilan's previous contributions) I enjoyed working with maybe. It makes for a simpler implementation and read, and approaches the Elixir-like zen I search when validating configurations.

@paulo-ferraz-oliveira
Copy link
Collaborator Author

Made some minor adjustment (latest commits) from adapting to rebar3_lint, but as I've identified it I'm only missing tests on elvis_core, at this moment.

@paulo-ferraz-oliveira paulo-ferraz-oliveira force-pushed the fix/config-parse-validate branch from cdf564c to 632b4d3 Compare July 24, 2025 19:24
@paulo-ferraz-oliveira paulo-ferraz-oliveira force-pushed the fix/config-parse-validate branch from 632b4d3 to 63d1355 Compare July 24, 2025 19:31
Copy link
Member

@elbrujohalcon elbrujohalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a quick review today (after a few days travelling around Europe 😎 )… I just left a nit-picky comment. I'll do a more in-depth review later.

@paulo-ferraz-oliveira
Copy link
Collaborator Author

I love the all new ✅ with Windows in the mix.

I can't simply validate rules as being non-empty
because it is, by default, so what I state is
"you either provide a non-empty list or a valid ruleset"
either_rules_is_nonempty_or_ruleset_is_defined(_Rules, Ruleset) when Ruleset =/= undefined ->
ok;
either_rules_is_nonempty_or_ruleset_is_defined(_Rules, _Ruleset) ->
{error, io_lib:format("either 'rules' is a non-empty list or 'ruleset' is defined.", [])}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe something like…

Suggested change
{error, io_lib:format("either 'rules' is a non-empty list or 'ruleset' is defined.", [])}.
{error, io_lib:format("each config section is expected to have either a non-empty list of 'rules' or a 'ruleset'.", [])}.

…would be clearer.

ok.

check_flag({Option, _What} = Obj) ->
table_exists() andalso ets:lookup(elvis_config, Option) =:= [Obj].
Copy link
Member

@elbrujohalcon elbrujohalcon Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not strongly against your code, but I would've written it as…

Suggested change
table_exists() andalso ets:lookup(elvis_config, Option) =:= [Obj].
try ets:lookup(elvis_config, Option) of
Result -> Result =:= [Obj]
catch
_:badarg -> false
end.

…or even…

Suggested change
table_exists() andalso ets:lookup(elvis_config, Option) =:= [Obj].
try
[Obj] =:= ets:lookup(elvis_config, Option)
catch
_:badarg -> false
end.

Comment on lines +791 to +796
case table_exists() of
false ->
_ = ets:new(Table, [public, named_table]);
_ ->
ok
end.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering my previous comments, this function may no longer be needed, but if it stays… It has a somewhat semantic issue: Line 791 checks if elvis_config table exists, but line 793 then creates a table called Table. Either both should be parametric or the function should receive no args.

NS = elvis_rule:ns(Rule),
Name = elvis_rule:name(Rule),
% Bypass new/ constraints.
DefKeys = maps:keys(NS:default(Name)) ++ [ignore],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DefKeys = maps:keys(NS:default(Name)) ++ [ignore],
DefKeys = [ignore | maps:keys(NS:default(Name))],

Copy link
Member

@elbrujohalcon elbrujohalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a bunch of comments here and there… instead of making a formal review, mostly because I was not really conducting a review… just reading the code diagonally :)

In other words: A little bit of nit-picking, I hope you don't mind 🫣

Copy link
Member

@elbrujohalcon elbrujohalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I finally had time to do a full review. All my previous comments stay, and I added yet another nit-picky one. Once all are addressed, this is mergeable to me.
Excellent job, as usual, @paulo-ferraz-oliveira !!

),
default_for(Key);
{ok, Value} ->
elvis_utils:debug("value for key '~s' found in application environment", [Key]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
elvis_utils:debug("value for key '~s' found in application environment", [Key]),
elvis_utils:debug("value for key '~s' (~p) found in application environment", [Key, Value]),

@paulo-ferraz-oliveira paulo-ferraz-oliveira changed the title Deeper configuration parsing and validation ≥x c´ Aug 3, 2025
@elbrujohalcon elbrujohalcon changed the title ≥x c´ Deeper configuration parsing and validation Aug 4, 2025
@elbrujohalcon
Copy link
Member

@paulo-ferraz-oliveira what's the status of this PR? Are you still working on it?

@elbrujohalcon
Copy link
Member

@paulo-ferraz-oliveira what's the status of this PR? Are you still working on it?

ghost-town

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate rule configs in elvis.config Improve elvis.config validation

3 participants